
function createStore(reducer, preLoadedState, enhancer) {
  // 约束 reducer 的类型
  if (typeof reducer !== 'function') {
    throw new Error('reducer 必须是函数')
  }
  // 判断 enhancer 是否传入
  if (typeof enhancer !== 'undefined') {
    // 判断 enhancer 是否是函数
    if (typeof enhancer !== 'function') {
      throw new Error('enhancer 必须是函数')
    }
    // 要求调用 enhancer 之后返回一个函数，然后将 reducer 和 preLoadedState 传入
    return enhancer(createStore)(reducer, preLoadedState)
  }
  // store 对象中存储的状态
  let currentState = preLoadedState
  // 存放订阅者函数
  const currentListeners = []
  // 获取状态
  function getState() {
    return currentState
  }
  // 因为要触发 action ，所以需要知道要触发哪个action，所以需要这个 action 参数
  function dispatch(action) {
    // 判断 action 是否是对象
    if (!isPlainObject(action)) {
      throw new Error('action 必须是对象')
    }
    // 判断对象中是否具有 type 属性
    if (typeof action.type === 'undefined') {
      throw new Error('action 中必须要有 type 属性')
    }
    // reducer函数需要两个参数，一个是 state, 一个是 action
    // reducer 内部会根据 action 的 type 属性值做对应的处理，然后返回最新的状态
    currentState = reducer(currentState, action)
    // 循环数组 调用订阅者函数
    for (let i = 0; i < currentListeners.length; i++) {
      const listener = currentListeners[i]
      listener()
    }
  }
  // 订阅状态
  function subscribe(listener) {
    currentListeners.push(listener)
  }

  return {
    getState,
    dispatch,
    subscribe
  }
}
// 判断 obj 是否是对象
function isPlainObject(obj) {
  // 排除基本数据类型和 null
  if (typeof obj !== 'object' || obj === null) {
    return false
  }
  // 区分数组和对象
  // 因为数组和对象的最顶层的原型链一定是对象
  // 如果是数组，那么它当前的原型对象是一个数组，也就是说它和最顶层的原型对象不相等
  // 如果是对象，它当前的原型对象是一个对象，和最顶层的原型对象相等
  // 获取 obj 的最顶层的原型对象
  let proto = obj
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }
  // 循环结束后，proto 中保存的就是 obj 最顶层的原型对象了
  // 判断当前原型对象和最顶层的原型对象是否相对
  return Object.getPrototypeOf(obj) === proto
}

function applyMiddleware(...middlewares) {
  return function (createStore) {
    return function (reducer, preLoadedState) {
      // 创建 store
      const store = createStore(reducer, preLoadedState)
      // 阉割版的 store
      const middlewareAPI = {
        getState: store.getState,
        dispatch: store.dispatch
      }
      // 调用中间件的第一层函数 传递阉割版的 store 对象
      // 调用完成后，chain中保存的就是中间件的里面两层函数
      const chain = middlewares.map(middleware => middleware(middlewareAPI))
      // 调用第二层函数
      const dispatch = compose(...chain)(store.dispatch)
      return {
        ...store,
        dispatch
      }
    }
  }
}

function compose() {
  const funcs = [...arguments]
  return function (dispatch) {
    for (let i = funcs.length - 1; i >= 0; i--) {
      dispatch = funcs[i](dispatch)
    }
    return dispatch
  }
}
// 函数的功能
function bindActionCreators(actionCreators, dispatch) {
  const boundActionCreators = {}
  for (let key in actionCreators) {
    boundActionCreators[key] = function () {
      dispatch(actionCreators[key]())
    }
  }
  return boundActionCreators
}

function combineReducers(reducers) {
  // 1. 检查 reducer 的类型，它必须是函数
  const reducerKeys = Object.keys(reducers)
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]
    if (typeof reducers[key] !== 'function') {
      throw new Error('reducer 必须是函数')
    }
  }
  // 2.调用一个一个小的 reducer，将每一个小的reducer中返回的状态存储在一个新的大的对象中
  return function (state, action) {
    const nextState = {}
    for (let i = 0; i < reducerKeys.length; i++) {
      const key = reducerKeys[i]
      const reducer = reducers[key]
      const preStateForKey = state[key]
      nextState[key] = reducer(preStateForKey, action)
    }
    return nextState
  }
}